#include "mesurementinterface.h"
#include "workpiecebuff.h"
#include <string>
#include <unistd.h>

#include <QDebug>
#include <QImage>
#include <QThread>
#include <QDateTime>
#include <QSettings>

#include "FitAdjust.hpp"

bool MeasurementInterface::m_isDataCorrect = false;

MeasurementInterface::MeasurementInterface(QObject *parent) : QObject(parent),
    m_pWorkpiece(nullptr),
    m_isDataPruning(false),
    m_isDataXmlSwitch(false),
    m_dataCheckNum(0)
{
}

MeasurementInterface::~MeasurementInterface()
{
    qDebug() << "MeasurementInterface start to destroy";
    if(nullptr != m_pWorkpiece) {
        delete m_pWorkpiece;
        m_pWorkpiece = nullptr;
    }
    qDebug() << "MeasurementInterface destroy finished";
}

bool MeasurementInterface::SetTemplatePath(
        const std::string &path)
{
    m_configPath = path;
    if(nullptr == m_pWorkpiece) {
        std::string configPath = (path + "/MeasureWorkpieceConfig.ini");

        QSettings configSetting(QString::fromStdString(configPath), QSettings::IniFormat);
        int workpieceType   = configSetting.value("measure_workpiece_info/measure_workpiece_type").toInt();
        int dataPruningType = configSetting.value("measure_workpiece_info/measure_data_pruning").toInt();
        int dataCorrect     = configSetting.value("measure_workpiece_info/measure_data_correct").toInt();
        int dataXmlSwitch   = configSetting.value("measure_workpiece_info/measure_xml_switch").toInt();
        int dataCheckNum  = configSetting.value("measure_workpiece_info/measure_check_num").toInt();

        m_dataCheckNum = dataCheckNum;

        if(1 == dataPruningType) {
            m_isDataPruning = true;
        }

        if(1 == dataCorrect) {
            m_isDataCorrect = true;
        }

        if(1 == dataXmlSwitch) {
            m_isDataXmlSwitch = true;
        }

        if(workpieceType <= 0) {
            qCritical() << "灰度算法: 工件类型配置参数错误";
            return false;
        }

        m_pWorkpiece = m_workpieceFactory.CreateWorkpiece(workpieceType);
        if(nullptr == m_pWorkpiece) {
            qCritical() << "灰度算法: 工件对象生成错误";
            return false;
        }

        connect(m_pWorkpiece,
                &MeasureWorkpiece::MesurementResultResponse,
                this,
                &MeasurementInterface::SlotMesurementResultResponse, Qt::DirectConnection);
    }
    return m_pWorkpiece->SetTemplatePath(path);
}

void MeasurementInterface::SetStandardVal(const std::map<string, MeasureItemStandardVal> &standardValMap)
{
    if(nullptr == m_pWorkpiece) {
        qCritical() << "workpiece null";
        return;
    }
    m_measureItemStandardVal = standardValMap;
    m_pWorkpiece->SetStandardVal(standardValMap);
}

void MeasurementInterface::SetWorkpieceInfo(const string &modeName, const string &workpieceType)
{
    if(nullptr == m_pWorkpiece) {
        qCritical() << "workpiece null";
        return;
    }

    m_pWorkpiece->SetWorkpieceInfo(modeName, workpieceType);
}

void MeasurementInterface::MesurementCalRequest(const QImage &image,
                                                const QPoint &datumOffset,
                                                int workpieceId,
                                                int pictureId,
                                                int channelId,
                                                int fixtureId)
{
    cv::Mat imageMat = QImageToCvMat(image);
    if(!imageMat.data) {
        qDebug() << "image empty";
        return;
    }
    qDebug() << "start to cache image";
    if(nullptr == m_pWorkpiece) {
        qCritical() << "workpiece measure empty workpiece";
        return ;
    }

    // 如果切换xml参数
    if(m_isDataXmlSwitch) {
        if(workpieceId <= m_dataCheckNum) {
            fixtureId = fixtureId + 2;
        }
    }

    m_pWorkpiece->CacheData(imageMat,
                            datumOffset,
                            workpieceId,
                            pictureId,
                            channelId,
                            fixtureId);
}

bool MeasurementInterface::GetDatumPixelDistance(const QImage &img,
                                                 int fixtureId,
                                                 QMap<QString, float> &pixelDistance)
{
    if(img.isNull()) {
        qDebug() << "input empty image";
        return false;
    }

    if(fixtureId != 0 && fixtureId != 1) {
        qDebug() << "error input fixtureId";
        return false;
    }

    cv::Mat imageMat = QImageToCvMat(img);
    if(!imageMat.data) {
        qDebug() << "image empty";
        return false;
    }

//    RectExtract rectExtract;
//    rectExtract.GetDatumDistance(imageMat, fixtureId,
//                                 verPixelDistance,
//                                 honPixelDistance);
    return true;

}

void MeasurementInterface::SlotMesurementResultResponse(int workpieceId,
                                                        int pictureId,
                                                        int channelId,
                                                        int fixtureId,
                                                        const std::map<std::string, float> &measureInfo,
                                                        const QMap<QString, QString> &error)
{
    qDebug() << "in SlotMesurementResultResponse";
    QMap<QString, MeasurementInterface::MeasureInfo> measureInfoDst;

    if(!measureInfo.empty()) {
        Map2QMap(measureInfo, fixtureId, measureInfoDst);
    }
    //

    emit MesurementCalResponse(workpieceId, pictureId, channelId, measureInfoDst, error);
    qDebug() << "SlotMesurementResultResponse send signal";
}

cv::Mat MeasurementInterface::QImageToCvMat(const QImage &image)
{
    switch (image.format())
    {
    case QImage::Format_ARGB32:
    case QImage::Format_ARGB32_Premultiplied:
    {
        cv::Mat mat( image.height()                         ,
                     image.width()                          ,
                     CV_8UC4                                ,
                     const_cast<uchar*>(image.bits())       ,
                     static_cast<size_t>(image.bytesPerLine())
                     );
        return mat.clone();
    }

    case QImage::Format_RGB32:
    case QImage::Format_RGB888:
    {
        QImage   swapped = image;
        if ( image.format() == QImage::Format_RGB32 )
        {
            swapped = swapped.convertToFormat( QImage::Format_RGB888 );
        }

        swapped = swapped.rgbSwapped();

        return cv::Mat( swapped.height(), swapped.width(),
                        CV_8UC3,
                        const_cast<uchar*>(swapped.bits()),
                        static_cast<size_t>(swapped.bytesPerLine())
                        ).clone();
    }

    case QImage::Format_Grayscale8:
    {
        cv::Mat  mat = cv::Mat(image.height(),
                               image.width(),
                               CV_8UC1,
                               (void*)image.constBits(),
                               image.bytesPerLine());
        return mat;
    }

    case QImage::Format_Indexed8:
    {
        cv::Mat  mat( image.height()                        ,
                      image.width()                         ,
                      CV_8UC1                               ,
                      const_cast<uchar*>(image.bits())      ,
                      static_cast<size_t>(image.bytesPerLine())
                      );
        return mat.clone();
    }

    default:
        qWarning() << "CVS::QImageToCvMat() - QImage format not handled in switch:"
                   << image.format();
        break;
    }

    return cv::Mat();
}

void MeasurementInterface::Map2QMap(const std::map<string, float> &mapInfo,
                                    int fixtureId,
                                    QMap<QString, MeasurementInterface::MeasureInfo> &qMapInfo)
{
    for (const auto &item : mapInfo)
    {
        QString &&name = QString::fromStdString(item.first);
        float dist = item.second;

        auto strlist = name.split('_');
        if (strlist.back() == "pixel")
            qMapInfo[strlist.first()].pixel_distance = dist;
        else {
            float tempDst = DataContraction(item.first, m_isDataPruning, dist);
            qMapInfo[strlist.first()].real_distance = tempDst;
        }
    }
}

float MeasurementInterface::DataContraction(const std::string &keyStr, bool flag, float dist)
{
    float tempVal = 0.0f;
    if(flag) {
        int startIndex = keyStr.find("_real");
        std::string keyStrTemp = keyStr.substr(0, startIndex);
        if(dist > m_measureItemStandardVal[keyStrTemp].upperTolerance ||
                dist < m_measureItemStandardVal[keyStrTemp].lowerTolerance) {
            tempVal = m_measureItemStandardVal[keyStrTemp].standardVal;
        }
        else {
            tempVal = dist;
        }
    }
    else {
        tempVal = dist;
    }
    return tempVal;
}
